//
//  OpenCLConvolutioner_mac.cpp
//  LowPassFilter_Standalone
//
//  Created by kadlubek47 on 02.05.2013.
//  Copyright (c) 2013 kadlubek47. All rights reserved.
//

#include "OpenCLConvolutioner_mac.h"

OpenCLConvolutioner_mac::OpenCLConvolutioner_mac () { initialize(); }

OpenCLConvolutioner_mac::~OpenCLConvolutioner_mac() { gcl_free(buffer_h_); gcl_free(buffer_M_); gcl_free(buffer_x_); gcl_free(buffer_y_); }

void OpenCLConvolutioner_mac::printDeviceInfo() {
    char name[128];
    cl_device_id gpu = gcl_get_device_id_with_dispatch_queue(queue);
    //
    clGetDeviceInfo(gpu, CL_DEVICE_NAME, 128, name, NULL);
    fprintf(stdout, "Created a dispatch queue using the %s\n", name);
    //
    cl_uint computeUnitsNumber;
    clGetDeviceInfo(gpu, CL_DEVICE_MAX_COMPUTE_UNITS, sizeof(computeUnitsNumber), &computeUnitsNumber, NULL);
    fprintf(stdout, "Max compute units %d\n", computeUnitsNumber);
    //
    cl_uint maxWorkGroupDimensions;
    clGetDeviceInfo(gpu, CL_DEVICE_MAX_WORK_ITEM_DIMENSIONS, sizeof(cl_uint), &maxWorkGroupDimensions, NULL);
    fprintf(stdout, "Maximum workgroup dimensions %d\n", maxWorkGroupDimensions);
    //
    size_t maxWorkGroupSize;
    clGetDeviceInfo(gpu, CL_DEVICE_MAX_WORK_GROUP_SIZE, sizeof(size_t), &maxWorkGroupSize, NULL);
    fprintf(stdout, "Maximum workgroup size %zd\n", maxWorkGroupSize);
    //
    cl_ulong globalMemorySize;
    clGetDeviceInfo(gpu, CL_DEVICE_GLOBAL_MEM_SIZE, sizeof(cl_ulong), &globalMemorySize, NULL);
    fprintf(stdout, "Global memory size %lld MB\n", globalMemorySize/1048576);
    //
    cl_ulong localMemorySize;
    clGetDeviceInfo(gpu, CL_DEVICE_LOCAL_MEM_SIZE, sizeof(cl_ulong), &localMemorySize, NULL);
    fprintf(stdout, "Local memory size %lld kB\n", localMemorySize/1024);
    //
    
}

void OpenCLConvolutioner_mac::initialize() {
    queue = gcl_create_dispatch_queue(CL_DEVICE_TYPE_GPU, NULL);
    printDeviceInfo();
}

void OpenCLConvolutioner_mac::setSizeOfDeviceBuffers(unsgn_integer_type x_size, unsgn_integer_type M) {   //< Prepare necessary buffers for GPU
    
    if( x_size==0 || M==0 )
        throw WrongFunArgVal_Exception();
    
    if(x_size==x_size_ && M==M_)
        return;
    
    if(x_size_ != 0)
    {
        gcl_free(buffer_x_);
        gcl_free(buffer_y_);
    }
    
    x_size_ = x_size;
    M_ = M;
    y_size_ = x_size_ - 2*M_;
    h_size_ = 2*M_ + 1;
    
    buffer_x_ = gcl_malloc(sizeof(cl_float) * x_size_, NULL, CL_MEM_READ_ONLY);
    buffer_y_ = gcl_malloc(sizeof(cl_float) * y_size_, NULL, CL_MEM_WRITE_ONLY);

    if(!buffersFirstTimeSet) {
        buffer_h_ = gcl_malloc(sizeof(cl_float) * h_size_, NULL, CL_MEM_READ_ONLY);
        buffer_M_ = gcl_malloc(sizeof(cl_uint) * 1, NULL, CL_MEM_READ_ONLY);
    }
    
    buffersFirstTimeSet=true;
    
}

//___________________________
#pragma mark Process function!
void OpenCLConvolutioner_mac::process(float_type* x, unsgn_integer_type x_size, float_type* h, unsgn_integer_type M, float_type* y) {
    setSizeOfDeviceBuffers(x_size, M);
    
    //for(int i=0; i < y_size_; ++i) y[i] = x[i];
    dispatch_sync(queue, ^{
        
        gcl_memcpy(buffer_x_, x, x_size_ * sizeof(float_type));
        gcl_memcpy(buffer_h_, h, h_size_ * sizeof(float_type));
        gcl_memcpy(buffer_M_, &M, 1 * sizeof(unsgn_integer_type));
        
        size_t wgs;
        gcl_get_kernel_block_workgroup_info(convolute_kernel,
                                            CL_KERNEL_WORK_GROUP_SIZE,
                                            sizeof(wgs), &wgs, NULL);
        
        cl_ndrange range = {
            1, // The number of dimensions to use.
            {0, 0, 0}, // The offset in each dimension. We want to
            {y_size_, 0, 0}, // The global range -- this is how many items
            {0}
        };
        
        convolute_kernel(&range,(cl_float*)buffer_x_, wgs, (cl_float*)buffer_h_, (cl_uint*)buffer_M_, (cl_float*)buffer_y_);
        //convolute_kernel(&range,(cl_float*)buffer_x_, (cl_float*)buffer_y_);
        
        gcl_memcpy(y, buffer_y_, sizeof(cl_float) * y_size_);
    });
    
}